Function.RegisterNamespace("System.Event");

System.Event.EventManager=function(){
    // Private members
    var _events={};
    
    // ctor
    function EventManager(type1,type2,typeN){
        if(arguments.length==0)throw new Error("System.EventManager.ctor: at least one type must be registered.");
        var types=Array.prototype.slice.call(arguments,0);
        Array.ForEach(types,addType);
    }
    EventManager.apply(this,arguments);
    
    // Public methods
    this.Add=function(type,handler){
        type=normalizeType(type);
        if(!_events[type])throw new Error(String.Format("System.EventManager.Add: the event type '{0}' is not registered.",type));
        if(!Object.IsType(Function,handler))throw new Error("System.EventManager.Add: 'handler' must be a valid Function pointer.");
        _events[type].push(handler);
    };

    this.Dispose=function(){
        for(var x in _events){
            this.Remove(x,null);
        }
    };
    
    this.Fire=function(type,context){
        type=normalizeType(type);
        if(!_events[type])throw new Error(String.Format("System.EventManager.Fire: the event type '{0}' is not registered.",type));
        if(!context)context={};
        context.Cancel=false;
        Array.ForEach(_events[type],firePredicate(context));
    };

    this.Get=function(type){
        type=normalizeType(type);
        if(!_events[type])throw new Error(String.Format("System.EventManager.Get: the event type '{0}' is not registered.",type));
        return _events[type].slice(0);        
    };
    
    this.Remove=function(type,handler){
        type=normalizeType(type);
        if(!_events[type])throw new Error(String.Format("System.EventManager.Remove: the event type '{0}' is not registered.",type));
        Array.ForEach(_events[type],removeType,handlerPredicate(handler));
    };
    
    // Private methods
    function addType(type,context){
        type=normalizeType(type);
        if(_events[type])return;
        _events[type]=[];
    }
    
    function normalizeType(type){
        if(type==undefined||!Object.IsType(Function,type.toString))throw new Error("System.EventManager.normalizeType: 'type' must be convertible to String.");
        return type.toString().toLowerCase();
    }

    function removeType(type,context){
        context.Instance.splice(context.Index,1);
        context.Index--;
    }
    
    // Predicates
    function firePredicate(eventContext){
        return function(method,context){
            method(eventContext);
            if(eventContext.Cancel)context.Cancel=true;
        };
    }
    
    function handlerPredicate(handler){
        return function(method,context){
            return !handler||handler==method;
        };
    }
};